using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Threading;
using Autofac;

namespace Catastrophe.Initialize.IOCProvier
{
    public class CatastropheProvider<T>
    {
        public static IContainer Instance { get; private set; }

        public static ContainerBuilder Builder { get; private set; }

        public static void Register(IContainer container)
        {
            if (CatastropheProvider<T>.Instance == null)
            {
                CatastropheProvider<T>.Instance = container;
            }
        }

        public static void Register(ContainerBuilder builder)
        {
            if (CatastropheProvider<T>.Builder == null)
            {
                CatastropheProvider<T>.Builder = builder;
            }
        }

        public static void Build()
        {
            if (CatastropheProvider<T>.Builder != null)
            {
                CatastropheProvider<T>.Instance = CatastropheProvider<T>.Builder.Build();
            }
        }

        public static void Get(out T result)
        {
            if (CatastropheProvider<T>.Instance == null)
            {
            }
            if (CatastropheProvider<T>.Instance.IsRegistered<T>())
            {
                result = CatastropheProvider<T>.Instance.Resolve<T>();
            }
            else
            {
                result = default(T);
            }
        }

        public static TClass Get<TClass>() where TClass : T
        {
            CatastropheProvider<T>.Get(out var result);
            if (result == null)
            {
                return default(TClass);
            }
            return (TClass)(object)result;
        }

        public static T Get()
        {
            CatastropheProvider<T>.Get(out var result);
            if (result == null)
            {
                return default(T);
            }
            return result;
        }
    }
    public class CatastropheProvider
    {
        private static IContainer container = null;

        private static ICacheProvider localCache;

        private static ConcurrentDictionary<string, IContainer> DicContainer = new ConcurrentDictionary<string, IContainer>();

        public static IContainer Instance { get; private set; }

        private static IContainer Container
        {
            get
            {
                if (CatastropheProvider.container == null)
                {
                    Mutex mutex = new Mutex();
                    mutex.WaitOne();
                    if (CatastropheProvider.container == null)
                    {
                        CatastropheProvider.InitRegister();
                    }
                    mutex.Close();
                }
                return CatastropheProvider.container;
            }
        }

        public static void Register(IContainer container)
        {
            if (CatastropheProvider.Instance == null)
            {
                CatastropheProvider.Instance = container;
            }
        }

        public static void Get<T>(out T result)
        {
            if (CatastropheProvider.Instance == null)
            {
            }
            result = CatastropheProvider.Instance.Resolve<T>();
        }

        public static T Get<T>()
        {
            CatastropheProvider.Get<T>(out var result);
            return result;
        }

        public static TClass Get<TInterface, TClass>() where TClass : TInterface
        {
            CatastropheProvider.Get<TInterface>(out var result);
            if (result == null)
            {
                return default(TClass);
            }
            return (TClass)(object)result;
        }

        private static void InitRegister()
        {
            ContainerBuilder containerBuilder = new ContainerBuilder();
            containerBuilder.RegisterType<LocalCacheProvider>().Named<ICacheProvider>("LocalCache");
            CatastropheProvider.container = containerBuilder.Build();
        }

        public static ICacheProvider GetLocalCache()
        {
            if (CatastropheProvider.localCache == null)
            {
                Mutex mutex = new Mutex();
                mutex.WaitOne();
                if (CatastropheProvider.localCache == null)
                {
                    try
                    {
                        CatastropheProvider.localCache = CatastropheProvider.Container.ResolveNamed<ICacheProvider>("LocalCache");
                    }
                    catch (Exception)
                    {
                        throw;
                    }
                }
                mutex.Close();
            }
            return CatastropheProvider.localCache;
        }

        public static void RegisterByKey(string key, IContainer container)
        {
            if (!CatastropheProvider.DicContainer.ContainsKey(key))
            {
                CatastropheProvider.DicContainer.TryAdd(key, container);
                return;
            }
            throw new Exception("容器" + key + "已经存在!");
        }

        public static IContainer GetIContainer(string key)
        {
            if (string.IsNullOrWhiteSpace(key))
            {
                return null;
            }
            if (CatastropheProvider.DicContainer.ContainsKey(key))
            {
                return CatastropheProvider.DicContainer[key];
            }
            return null;
        }

        public static IContainer GetAssemblyIContainer()
        {
            Assembly callingAssembly = Assembly.GetCallingAssembly();
            string name = callingAssembly.GetName().Name;
            if (CatastropheProvider.DicContainer.ContainsKey(name))
            {
                return CatastropheProvider.DicContainer[name];
            }
            return null;
        }

        public static BaseResponse GetByAssembly<T>(out T result)
        {
            Assembly callingAssembly = Assembly.GetCallingAssembly();
            string name = callingAssembly.GetName().Name;
            return CatastropheProvider.GetByKey<T>(name, out result);
        }

        public static BaseResponse GetByKey<T>(string key, out T result)
        {
            BaseResponse baseResponse = new BaseResponse();
            try
            {
                if (CatastropheProvider.DicContainer.ContainsKey(key))
                {
                    if (CatastropheProvider.DicContainer[key].IsRegistered<T>())
                    {
                        result = CatastropheProvider.DicContainer[key].Resolve<T>();
                        baseResponse.Success = true;
                        return baseResponse;
                    }
                    result = default(T);
                    baseResponse.Message = typeof(T).Name + " not register!";
                    return baseResponse;
                }
                result = default(T);
                baseResponse.Message = key + " not exist!";
                return baseResponse;
            }
            catch (Exception ex)
            {
                result = default(T);
                baseResponse.Message = ex.ToString();
                return baseResponse;
            }
        }

        public static BaseResponse<T> GetByKey<T>(string key)
        {
            BaseResponse<T> baseResponse = new BaseResponse<T>();
            T result;
            BaseResponse byKey = CatastropheProvider.GetByKey<T>(key, out result);
            baseResponse.Success = byKey.Success;
            baseResponse.Message = byKey.Message;
            baseResponse.Content = result;
            return baseResponse;
        }

        public static BaseResponse<T> GetByAssembly<T>()
        {
            Assembly callingAssembly = Assembly.GetCallingAssembly();
            string name = callingAssembly.GetName().Name;
            return CatastropheProvider.GetByKey<T>(name);
        }

        public static BaseResponse<TInterface> GetByKey<TInterface, TClass>(string key) where TClass : TInterface
        {
            BaseResponse<TInterface> baseResponse = new BaseResponse<TInterface>();
            TInterface result;
            BaseResponse byKey = CatastropheProvider.GetByKey<TInterface>(key, out result);
            baseResponse.Success = byKey.Success;
            baseResponse.Message = byKey.Message;
            baseResponse.Content = result;
            return baseResponse;
        }

        public static BaseResponse<TInterface> GetByAssembly<TInterface, TClass>() where TClass : TInterface
        {
            Assembly callingAssembly = Assembly.GetCallingAssembly();
            string name = callingAssembly.GetName().Name;
            return CatastropheProvider.GetByKey<TInterface, TClass>(name);
        }

        public static BaseResponse<T> GetByName<T>(string key, string name)
        {
            BaseResponse<T> baseResponse = new BaseResponse<T>();
            try
            {
                
                if (CatastropheProvider.DicContainer.ContainsKey(key))
                {
                    var con = CatastropheProvider.DicContainer[key];
                    if (con != null)
                    {
                        if (con.IsRegistered<T>())
                        {
                            baseResponse.Content = con.ResolveNamed<T>(name);
                            if (baseResponse.Content!=null)
                            {
                                baseResponse.Success = true;
                            }
                            return baseResponse;
                        }
                        baseResponse.Message = name + " not register!";
                        return baseResponse;
                    }
                       
                }
                baseResponse.Message = key + " not exist!";
                return baseResponse;
            }
            catch (Exception ex)
            {
                baseResponse.Message = ex.ToString();
                return baseResponse;
            }
        }

        public static BaseResponse<T> GetByName<T>(string name)
        {
            Assembly callingAssembly = Assembly.GetCallingAssembly();
            string name2 = callingAssembly.GetName().Name;
            return CatastropheProvider.GetByName<T>(name2, name);
        }
    }
}
